#include "compositemodulegenerator.hpp"

//TODO remove iostream
#include <iostream>
#include <nmlib/model/module.hpp>
#include <nmlib/model/moduletype.hpp>
#include <nmlib/model/moduleinput.hpp>
#include <nmlib/model/moduleoutput.hpp>
#include <nmlib/model/signaltype.hpp>
#include <nmlib/codegeneration/inlinegenerator.hpp>

namespace nm {

CompositeModuleGenerator::CompositeModuleGenerator(const Module &module):
    m_module(module)
{
}

void CompositeModuleGenerator::generateDefaults(InlineGenerator &gen, std::ostream &out)
{
    //just use unlinkeddefaults
    UnlinkedValueDefaultsGenerator defaultsGenerator(m_module);
    defaultsGenerator.generateDefaults(gen, out);
}

void CompositeModuleGenerator::generateBody(InlineGenerator &gen, std::ostream &out)
{
    const ModuleType& moduleType = m_module.getType();
    const Module* inputModule = moduleType.getInputModule();
    const Module* outputModule = moduleType.getOutputModule();
    out << "\n////////////////////////////////////////////\n";
    out <<   "//Code generated by CompositeModuleGenerator\n";
    out <<   "////////////////////////////////////////////\n";

    //there used to be horrible naming conflicts
    //so create some alternatives that are unique
    //and ressign outside the scope
    struct Reassignment {
        std::string shadowedName;
        std::string uniqueName;
        SignalType signalType;
    };
    std::vector<Reassignment> inputReassignments; //this is for resolving shadowing
    std::vector<InlineGenerator::InputRemap> inputRemaps; //this is for binding to module inputs
    for(unsigned i=0; i<moduleType.numInputs(); ++i){
        const ModuleInput* moduleInput = moduleType.getInput(i);
        std::string shadowed = moduleInput->getName(); //the shadowed name
        std::string unique = gen.getUniqueId(); // the unique name we will use instead
        inputReassignments.push_back({shadowed, unique, moduleInput->getSignalType()});
        const InputLink* inputLink = inputModule->getInput(shadowed);
        inputRemaps.push_back(InlineGenerator::InputRemap{unique, inputLink});
    }
    std::vector<Reassignment> outputReassignments; //this is for resolving shadowing
    std::vector<InlineGenerator::OutputRemap> outputRemaps; //this is for binding to module outputs
    for(unsigned i=0; i<moduleType.numOutputs(); ++i){
        const ModuleOutput* moduleOutput = moduleType.getOutput(i);
        std::string shadowed = moduleOutput->getName(); //the shadowed name
        std::string unique = gen.getUniqueId(); // the unique name we will use instead
        outputReassignments.push_back({shadowed, unique, moduleOutput->getSignalType()});
        const OutputLink* outputLink = outputModule->getOutput(shadowed);
        outputRemaps.push_back(InlineGenerator::OutputRemap{unique, outputLink});
    }

    //declare and reassign inputs to their new names
    for(auto r : inputReassignments){
        Declaration d{
            r.signalType,
            r.uniqueName
        };
        d.gen(gen, out);
        Assignment a{
            r.uniqueName, r.shadowedName
        };
        a.gen(gen, out);
    }

    gen.generateFromLinks(inputRemaps, outputRemaps, out);

    //assign the answers to the previously shadowed variables
    for(auto r : outputReassignments){
        Declaration d{
            r.signalType,
            r.shadowedName
        };
        d.gen(gen, out);
    }
    for(auto r : outputReassignments){
        Assignment a{
            r.shadowedName, r.uniqueName
        };
        a.gen(gen, out);
    }
}

} // namespace nm
